﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using Nvigorate.Common;
using Nvigorate.Common.Reflection;

namespace Nvigorate.MVC
{
    public class Markup : List<string>
    {
        public Markup Prepend(Markup markup)
        {
            Insert(0, markup.ToString());
            return this;
        }

        public Markup Append(Markup markup)
        {
            Add(markup.ToString());
            return this;
        }

        public Markup()
        {
        }

        public Markup(string content)
        {
            Add(content);
        }

        public override string ToString()
        {
            return DelimitedBuilder.Construct(this, "");
        }
    }

    public abstract class Element<TElement> : Markup
        where TElement : Element<TElement>
    {
        protected string _tagName = "";
        protected List<Tuple<string, string>> _attributes = new List<Tuple<string, string>>();
        protected List<Markup> _contents = new List<Markup>();

        private static readonly string REGULAR_ATTRIBUTE_FORMAT = "{0}=\"{1}\"";
        private static readonly string QUOTED_ATTRIBUTE_FORMAT = "{0}='{1}'";

        protected string ProcessAttribute(Tuple<string, string> attributeTuple)
        {
            return string.Format(
                attributeTuple.Value2.Contains("\"")
                    ?
                        QUOTED_ATTRIBUTE_FORMAT
                    : REGULAR_ATTRIBUTE_FORMAT,
                attributeTuple.Value1, attributeTuple.Value2);
        }

        public TElement AddContent(Markup content)
        {
            _contents.Add(content);
            return (TElement) this;
        }

        public TElement AddContent(string content)
        {
            var html = new Markup(content);
            _contents.Add(html);
            return (TElement)this;
        }

        public TElement Id(string id)
        {
            _attributes.Add(Tuple.Create("id", id));
            return (TElement) this;
        }

        public TElement Class(string className)
        {
            _attributes.Add(Tuple.Create("class", className));
            return (TElement)this;
        }

        protected virtual string Translate()
        {
            var builder = new DelimitedBuilder(" ");
            builder.AppendFormat("<{0} {1}>{2}</{0}>",
                    _tagName,
                    DelimitedBuilder.Construct(_attributes.Select<Tuple<string,string>,string>(ProcessAttribute), " "),
                    DelimitedBuilder.Construct(_contents.Select(c => c.ToString()), " ")
                );
            return builder.ToString();
        }

        public override string ToString()
        {
            return Translate();
        }

        public Element(string tagName)
        {
            _tagName = tagName;    
        }
    }

    public class Paragraph : Element<Paragraph>
    {
        public Paragraph(Markup content) : base("p")
        {
            _contents.Add(content);
        }

        public Paragraph(string content)
            : base("p")
        {
            _contents.Add(new Markup(content));
        }
    }

    public class Link : Element<Link>
    {
        public Link(Markup content)
            : base("a")
        {
            _contents.Add(content);
        }

        public Link(string content)
            : base("a")
        {
            _contents.Add(new Markup(content));
        }
    }

    public class Span : Element<Span>
    {
        public Span() : base ("span")
        {
        }

        public Span(string content)
            : base("span")
        {
            AddContent(content);
        }

        public Span(Markup content)
            : base("span")
        {
            AddContent(content);
        }
    }

    public class Div : Element<Span>
    {
        public Div()
            : base("div")
        {
        }

        public Div(string content)
            : base("div")
        {
            AddContent(content);
        }

        public Div(Markup content)
            : base("div")
        {
            AddContent(content);
        }
    }

    public class Image : Element<Span>
    {
        

        public Image(string source)
            : base("img")
        {
           _attributes.Add(Tuple.Create("src", source));
        }
    }

    public static class ControlExtenders
    {
        private static string RetrieveModelProperty<TModel, TProperty>(Expression<Func<TModel, TProperty>> property)
        {
            var reference = property.Body as MemberExpression;
            if(reference == null)
                throw new MemberAccessException("The expression provided was not a valid member expression.");
            return reference.Member.Name;
        }

        private static TProperty ReadModelProperty<TModel, TProperty>(TModel modelInstance, Expression<Func<TModel, TProperty>> property)
        {
            return (TProperty) Reflector.Read(modelInstance, RetrieveModelProperty(property));
        }

        public static Span Span<TModel, TProperty>(this HtmlHelper<TModel> helper, TModel modelInstance, Expression<Func<TModel, TProperty>> property)
            where TModel : class
        {
            return new Span(ReadModelProperty(modelInstance, property).ToString());
        }

        public static Span Span<TEnumerable, TModel, TProperty>(this HtmlHelper<TEnumerable> helper, TModel modelInstance, Expression<Func<TModel, TProperty>> property)
            where TModel : class
            where TEnumerable : class, IEnumerable<TModel>
        {
            return new Span(ReadModelProperty(modelInstance, property).ToString());
        }

        public static Image Image<TModel, TProperty>(this HtmlHelper<TModel> helper, TModel modelInstance, Expression<Func<TModel, TProperty>> property)
            where TModel : class
        {
            return new Image(ReadModelProperty(modelInstance, property).ToString());
        }

        public static Image Image<TEnumerable, TModel, TProperty>(this HtmlHelper<TEnumerable> helper, TModel modelInstance, Expression<Func<TModel, TProperty>> property)
            where TModel : class
            where TEnumerable : class, IEnumerable<TModel>
        {
            return new Image(ReadModelProperty(modelInstance, property).ToString());
        }


    }
}
